#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <signal.h>

#define PCM_DEVICE "default"
#define CMD_PIPE "/tmp/pcm_cmd"

volatile sig_atomic_t stop_flag = 0;
volatile sig_atomic_t pause_flag = 0;

void *command_listener(void *arg) {
    char command[16];
    int fd;

    mkfifo(CMD_PIPE, 0666);

    while (!stop_flag) {
        fd = open(CMD_PIPE, O_RDONLY);
        if (fd < 0) {
            usleep(100000);
            continue;
        }

        ssize_t n = read(fd, command, sizeof(command) - 1);
        if (n > 0) {
            command[n] = '\0';
            if (strncmp(command, "haps", 4) == 0) {
                stop_flag = 1;
            } else if (strncmp(command, "pause", 5) == 0) {
                pause_flag = 1;
            } else if (strncmp(command, "resume", 6) == 0) {
                pause_flag = 0;
            }
        }

        close(fd);
        usleep(100000);
    }

    return NULL;
}

int play_pcm_file(const char *filename, snd_pcm_t *pcm_handle, int frame_bytes, int buffer_size, char *buffer) {
    FILE *fp = fopen(filename, "rb");
    if (!fp) return -1;

    while (!stop_flag) {
        if (pause_flag) {
            usleep(100000);
            continue;
        }

        size_t read_bytes = fread(buffer, 1, buffer_size, fp);
        if (read_bytes == 0) break;

        int written = snd_pcm_writei(pcm_handle, buffer, read_bytes / frame_bytes);
        if (written == -EPIPE) {
            snd_pcm_prepare(pcm_handle);
        } else if (written < 0) {
            break;
        }
    }

    fclose(fp);
    return 0;
}

int cmp_str(const void *a, const void *b) {
    return strcmp(*(const char **)a, *(const char **)b);
}

int main(int argc, char *argv[]) {
    if (argc != 2) return 1;

    pid_t pid = fork();
    if (pid < 0) return 1;
    if (pid > 0) return 0;

    setsid();

    pthread_t cmd_thread;
    pthread_create(&cmd_thread, NULL, command_listener, NULL);

    snd_pcm_t *pcm_handle;
    snd_pcm_hw_params_t *params;
    snd_pcm_uframes_t frames;
    int rc;

    rc = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0);
    if (rc < 0) return 1;

    snd_pcm_hw_params_malloc(&params);
    snd_pcm_hw_params_any(pcm_handle, params);
    snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(pcm_handle, params, 2);
    unsigned int rate = 44100;
    snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, NULL);
    snd_pcm_hw_params(pcm_handle, params);
    snd_pcm_hw_params_get_period_size(params, &frames, NULL);

    int frame_bytes = 2 * 2;
    int buffer_size = frames * frame_bytes;
    char *buffer = (char *) malloc(buffer_size);

    struct stat path_stat;
    stat(argv[1], &path_stat);

    if (S_ISDIR(path_stat.st_mode)) {
        DIR *dir = opendir(argv[1]);
        if (!dir) return 1;

        struct dirent *entry;
        char *files[1024];
        int count = 0;

        while ((entry = readdir(dir)) != NULL && count < 1024) {
            if (strstr(entry->d_name, ".pcm")) {
                char *full_path = malloc(4096);
                snprintf(full_path, 4096, "%s/%s", argv[1], entry->d_name);
                files[count++] = full_path;
            }
        }

        closedir(dir);
        qsort(files, count, sizeof(char *), cmp_str);

        for (int i = 0; i < count && !stop_flag; i++) {
            play_pcm_file(files[i], pcm_handle, frame_bytes, buffer_size, buffer);
            free(files[i]);
        }
    } else {
        play_pcm_file(argv[1], pcm_handle, frame_bytes, buffer_size, buffer);
    }

    snd_pcm_drain(pcm_handle);
    snd_pcm_close(pcm_handle);
    free(buffer);
    unlink(CMD_PIPE);
    return 0;
}
